package com.sz.netty.transport;

import com.sz.netty.entity.DtuFtpInfo;
import com.sz.netty.entity.DtuFtpMessageDetail;
import com.sz.netty.entity.DtuFtpMessageLog;
import com.sz.netty.entity.DtuTransportInfo;
import com.sz.netty.service.DeviceCommandService;
import com.sz.netty.service.DtuFtpInfoService;
import com.sz.netty.service.DtuFtpMessageLogService;
import com.sz.netty.service.DtuTransportInfoService;
import com.sz.netty.util.TransportEventLogger;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Transport analyzer for calculating start and end points
 */
@Slf4j
@Component
public class TransportAnalyzer {
    
    // Device transport context map
    private static final Map<String, TransportContext> transportContextMap = new ConcurrentHashMap<>();
    
    // Services
    private static DtuFtpInfoService dtuFtpInfoService;
    private static DtuFtpMessageLogService dtuFtpMessageLogService;
    private static DtuTransportInfoService dtuTransportInfoService;
    private static DeviceCommandService deviceCommandService;
    private static RabbitTemplate rabbitTemplate;
    
    // Static instance for static method calls
    private static TransportAnalyzer instance;
    
    @Autowired
    public TransportAnalyzer(DtuFtpInfoService dtuFtpInfoService, 
                            DtuFtpMessageLogService dtuFtpMessageLogService,
                            DtuTransportInfoService dtuTransportInfoService,
                            DeviceCommandService deviceCommandService,
                            RabbitTemplate rabbitTemplate) {
        TransportAnalyzer.dtuFtpInfoService = dtuFtpInfoService;
        TransportAnalyzer.dtuFtpMessageLogService = dtuFtpMessageLogService;
        TransportAnalyzer.dtuTransportInfoService = dtuTransportInfoService;
        TransportAnalyzer.deviceCommandService = deviceCommandService;
        TransportAnalyzer.rabbitTemplate = rabbitTemplate;
        TransportAnalyzer.instance = this;
    }
    
    /**
     * Process a new message detail record
     * 
     * @param messageDetail the message detail record
     */
    public void processMessageDetailNonStatic(DtuFtpMessageDetail messageDetail) {
        try {
            log.debug("Processing message detail: id={}, speed={}, vibrationStatus={}, radarDistance={}, time={}",
                    messageDetail.getId(), messageDetail.getSpeed(), messageDetail.getVibrationStatus(),
                    messageDetail.getRadarDistance(), messageDetail.getTime());
            
            // Get the device ID from the associated message log
            Long ftpMessageLogId = messageDetail.getFtpMessageLogId();
            if (ftpMessageLogId == null) {
                log.warn("FTP message log ID is null, skipping transport analysis");
                return;
            }
            
            // Get the message log to extract device ID
            DtuFtpMessageLog messageLog = dtuFtpMessageLogService.getById(ftpMessageLogId);
            if (messageLog == null) {
                log.warn("Message log not found for ID: {}, skipping transport analysis", ftpMessageLogId);
                return;
            }
            
            String deviceId = messageLog.getDtuNumber();
            if (deviceId == null || deviceId.isEmpty()) {
                log.warn("Device ID is null or empty, skipping transport analysis");
                return;
            }
            
            // Get device configuration parameters
            DtuFtpInfo deviceInfo = dtuFtpInfoService.getOne(
                new com.mybatisflex.core.query.QueryWrapper()
                    .where("device_id = ?", deviceId));
            
            if (deviceInfo == null) {
                log.warn("Device info not found for device ID: {}, skipping transport analysis", deviceId);
                return;
            }
            
            log.debug("Device {} configuration: tSpeed={}, tTimeZone={}, tTimes={}, tRadarDistance={}, tRadarTime={}",
                    deviceId, deviceInfo.getTSpeed(), deviceInfo.getTTimeZone(), deviceInfo.getTTimes(),
                    deviceInfo.getTRadarDistance(), deviceInfo.getTRadarTime());
            
            // Get or create transport context for this device
            TransportContext context = transportContextMap.computeIfAbsent(deviceId, 
                    id -> {
                        TransportContext ctx = new TransportContext();
                        ctx.setDeviceId(id);
                        return ctx;
                    });
            
            // Update last update time
            context.setLastUpdateTime(new Date());
            
            log.debug("Device {} context state: hasUnpairedStart={}, vibrationRecords={}, radarRecords={}", 
                    context.getDeviceId(), context.isHasUnpairedStart(), 
                    context.getVibrationRecords().size(), context.getRadarRecords().size());
            
            // Clean up old records in sliding windows
            if (deviceInfo.getTTimeZone() != null && deviceInfo.getTRadarTime() != null) {
                log.debug("Cleaning up old records for device {}: tTimeZone={}s, tRadarTime={}s", 
                        context.getDeviceId(), deviceInfo.getTTimeZone(), deviceInfo.getTRadarTime());
                context.cleanupOldRecords(deviceInfo.getTTimeZone(), deviceInfo.getTRadarTime());
                log.debug("After cleanup - vibrationRecords={}, radarRecords={}", 
                        context.getVibrationRecords().size(), context.getRadarRecords().size());
            }
            
            // Add records to sliding windows
            // According to protocol: Bit3 = 0 means vibration switch is closed (device is vibrating)
            // Bit3 = 1 means vibration switch is open (device is not vibrating)
            // Modified: Only add vibration records when device is vibrating AND speed is <= t_speed
            if (messageDetail.getVibrationStatus() != null && !messageDetail.getVibrationStatus() && 
                messageDetail.getTime() != null && 
                messageDetail.getSpeed() != null && deviceInfo.getTSpeed() != null &&
                messageDetail.getSpeed().doubleValue() < deviceInfo.getTSpeed().doubleValue()) {
                VibrationRecord vibrationRecord = new VibrationRecord();
                vibrationRecord.setTimestamp(messageDetail.getTime());
                vibrationRecord.setSpeed(messageDetail.getSpeed() != null ? messageDetail.getSpeed().doubleValue() : null);
                context.addVibrationRecord(vibrationRecord);
                log.debug("Added vibration record for device {}: vibrationStatus={}, speed={}, time={}", 
                        context.getDeviceId(), messageDetail.getVibrationStatus(), messageDetail.getSpeed(), messageDetail.getTime());
            } else {
                log.debug("Skipped adding vibration record for device {}: vibrationStatus={}, speed={}, time={}", 
                        context.getDeviceId(), messageDetail.getVibrationStatus(), messageDetail.getSpeed(), messageDetail.getTime());
            }
            
            if (messageDetail.getRadarDistance() != null && messageDetail.getTime() != null) {
                RadarRecord radarRecord = new RadarRecord();
                radarRecord.setTimestamp(messageDetail.getTime());
                radarRecord.setRadarDistance(messageDetail.getRadarDistance());
                context.addRadarRecord(radarRecord);
                log.debug("Added radar record for device {}: radarDistance={}, time={}", 
                        context.getDeviceId(), messageDetail.getRadarDistance(), messageDetail.getTime());
                log.debug("Total radar records for device {} after addition: {}", 
                        context.getDeviceId(), context.getRadarRecords().size());
            }
            
            // If we have an unpaired start point, track this GPS point for precise distance calculation
            if (context.isHasUnpairedStart() && 
                messageDetail.getLongitude() != null && messageDetail.getLatitude() != null &&
                messageDetail.getTime() != null) {
                TransportPoint gpsPoint = new TransportPoint();
                gpsPoint.setRecordId(messageDetail.getId());
                gpsPoint.setTimestamp(messageDetail.getTime());
                gpsPoint.setLongitude(messageDetail.getLongitude());
                gpsPoint.setLatitude(messageDetail.getLatitude());
                context.addGpsPoint(gpsPoint);
                log.debug("Added GPS point for precise distance calculation: recordId={}, time={}, lat={}, lon={}",
                        messageDetail.getId(), messageDetail.getTime(), messageDetail.getLatitude(), messageDetail.getLongitude());
            }
            
            // Check if this record can be a start point
            checkStartPoint(context, messageDetail, deviceInfo);
            
            // Check if this record can be an end point
            checkEndPoint(context, messageDetail, deviceInfo);
            
        } catch (Exception e) {
            log.error("Error processing message detail for transport analysis", e);
        }
    }
    
    /**
     * Static method to process a new message detail record
     * 
     * @param messageDetail the message detail record
     */
    public static void processMessageDetail(DtuFtpMessageDetail messageDetail) {
        if (instance != null) {
            instance.processMessageDetailNonStatic(messageDetail);
        } else {
            log.warn("TransportAnalyzer instance is null, skipping transport analysis");
        }
    }
    
    /**
     * Check if the current record satisfies start point conditions
     */
    private void checkStartPoint(TransportContext context, DtuFtpMessageDetail messageDetail, DtuFtpInfo deviceInfo) {
        try {
            log.debug("Checking start point for device {}: hasUnpairedStart={}", 
                    context.getDeviceId(), context.isHasUnpairedStart());
            
            // Skip if there's already an unpaired start point
            if (context.isHasUnpairedStart()) {
                log.debug("Device {} already has an unpaired start point, skipping start point check", context.getDeviceId());
                return;
            }
            
            // Check if we have all required parameters for start point detection
            if (deviceInfo.getTSpeed() == null || deviceInfo.getTTimeZone() == null || deviceInfo.getTTimes() == null) {
                log.debug("Missing transport parameters for device {}, skipping start point check", context.getDeviceId());
                return;
            }
            
            log.debug("Device {} parameters: tSpeed={}, tTimeZone={}, tTimes={}", 
                    context.getDeviceId(), deviceInfo.getTSpeed(), deviceInfo.getTTimeZone(), deviceInfo.getTTimes());
            
            // Check speed condition (speed <= t_speed)
            if (!isSpeedConditionMet(messageDetail, deviceInfo)) {
                log.debug("Speed condition not met for device {}, skipping start point check", context.getDeviceId());
                return;
            }
            
            // Check vibration count condition (count >= t_times in t_time_zone window)
            if (!isVibrationCountConditionMet(context, deviceInfo)) {
                log.debug("Vibration count condition not met for device {}, skipping start point check", context.getDeviceId());
                return;
            }
            
            // Check start point distance condition (distance from previous end point >= t_es_min_distance)
            if (!isStartPointDistanceConditionMet(context, messageDetail, deviceInfo)) {
                log.debug("Start point distance condition not met for device {}, skipping start point check", context.getDeviceId());
                return;
            }
            
            // Check radar distance condition (radar distance must not be greater than t_radar_distance)
            if (!isRadarDistanceConditionMet(messageDetail, deviceInfo)) {
                log.debug("Radar distance condition not met for device {}, skipping start point check", context.getDeviceId());
                return;
            }
            
            log.debug("All start point conditions met for device {}, recording start point", context.getDeviceId());
            
            // All conditions met, record as start point
            TransportPoint startPoint = new TransportPoint();
            startPoint.setRecordId(messageDetail.getId());
            startPoint.setTimestamp(messageDetail.getTime());
            startPoint.setLongitude(messageDetail.getLongitude());
            startPoint.setLatitude(messageDetail.getLatitude());
            startPoint.setSpeed(messageDetail.getSpeed());
            startPoint.setRadarDistance(messageDetail.getRadarDistance());
            
            context.setStartPoint(startPoint);
            context.setHasUnpairedStart(true);
            // Clear any previous GPS points as we're starting a new transport segment
            context.clearGpsPoints();
            
            // Track transport times and send messages to device
            // Get transport times for valid transports only
            int transportTimes = getTransportTimesForToday(context.getDeviceId());
            
            // Log start point event
            TransportEventLogger.logStartPointEvent(context.getDeviceId(), messageDetail, deviceInfo, transportTimes);
            
            // Log detailed vibration data for start point analysis
            TransportEventLogger.logStartPointVibrationData(context.getDeviceId(), context, deviceInfo);
            
            // Send message to device based on transport times
            if (deviceCommandService != null && deviceCommandService.isDeviceConnected(context.getDeviceId())) {
                // For start point, send 90X where X is the next transport count
                String message = String.format("%03d", transportTimes + 1 + 900); // 901, 902, etc.
                
                // Try to send the command with acknowledgment waiting and retry logic
                boolean commandSent = false;
                int retryCount = 0;
                final int maxRetries = 6;
                
                while (!commandSent && retryCount < maxRetries) {
                    if (retryCount > 0) {
                        // Add a small delay between retries
                        try {
                            Thread.sleep(6000); // Wait 6 seconds between retries
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            break;
                        }
                        log.debug("Retrying to send transport start message '{}' to device {} (attempt {})", 
                                message, context.getDeviceId(), retryCount + 1);
                    }
                    
                    // Send command and wait for acknowledgment
                    commandSent = deviceCommandService.sendRemoteDisplayContentWithAck(context.getDeviceId(), message);
                    retryCount++;
                }
                
                if (commandSent) {
                    log.info("Sent transport start message '{}' to device {} (acknowledged)", message, context.getDeviceId());
                } else {
                    log.warn("Failed to send transport start message '{}' to device {} after {} attempts (no acknowledgment)", 
                            message, context.getDeviceId(), maxRetries);
                }
                
                // Log device command event
                TransportEventLogger.logDeviceCommandEvent(context.getDeviceId(), message, "Start point transport count message");
            }
            
            log.info("Recorded start point for device {}: recordId={}, time={}, transportTimes={}", 
                    context.getDeviceId(), startPoint.getRecordId(), startPoint.getTimestamp(), transportTimes + 1);
        } catch (Exception e) {
            log.error("Error checking start point for device {}", context.getDeviceId(), e);
        }
    }
    
    /**
     * Check if the current record satisfies end point conditions
     */
    private void checkEndPoint(TransportContext context, DtuFtpMessageDetail messageDetail, DtuFtpInfo deviceInfo) {
        try {
            // Skip if there's no unpaired start point
            if (!context.isHasUnpairedStart()) {
                log.debug("No unpaired start point for device {}, skipping end point check", context.getDeviceId());
                return;
            }
            
            log.debug("Checking end point for device {}: radarDistance={}, time={}", 
                    context.getDeviceId(), messageDetail.getRadarDistance(), messageDetail.getTime());
            
            // Check if we have all required parameters for end point detection
            if (deviceInfo.getTRadarDistance() == null || deviceInfo.getTRadarTime() == null) {
                log.debug("Missing radar parameters for device {}, skipping end point check", context.getDeviceId());
                return;
            }
            
            log.debug("Device {} radar parameters: tRadarDistance={}, tRadarTime={}", 
                    context.getDeviceId(), deviceInfo.getTRadarDistance(), deviceInfo.getTRadarTime());
            
            // Check radar condition (radar distance > t_radar_distance maintained for t_radar_time seconds)
            if (!isRadarConditionMet(context, deviceInfo)) {
                log.debug("Radar condition not met for device {}, skipping end point check", context.getDeviceId());
                return;
            }
            
            log.debug("All end point conditions met for device {}, recording end point", context.getDeviceId());
            
            // Get start and end points
            TransportPoint startPoint = context.getStartPoint();
            TransportPoint endPoint = new TransportPoint();
            endPoint.setRecordId(messageDetail.getId());
            endPoint.setTimestamp(messageDetail.getTime());
            endPoint.setLongitude(messageDetail.getLongitude());
            endPoint.setLatitude(messageDetail.getLatitude());
            endPoint.setSpeed(messageDetail.getSpeed());
            endPoint.setRadarDistance(messageDetail.getRadarDistance());
            
            // Validate that both start and end points have timestamps
            if (startPoint.getTimestamp() == null || endPoint.getTimestamp() == null) {
                log.warn("Missing timestamp for start or end point for device {}, skipping transport validation", context.getDeviceId());
                // Clear the start point to avoid accumulation of invalid states
                context.setHasUnpairedStart(false);
                context.setStartPoint(null);
                context.clearGpsPoints();
                return;
            }
            
            // Calculate precise transport distance using accumulated GPS points
            double preciseTransportDistance = context.calculatePreciseTransportDistance(startPoint, endPoint);
            
            // Convert to kilometers and truncate to 2 decimal places (without rounding)
            // Using Math.floor(value * 100) / 100000.0 to truncate to exactly two decimal places
            double transportDistanceKm = Math.floor(preciseTransportDistance * 100) / 100000.0;
            
            // Calculate transport time in seconds
            long transportTime = (endPoint.getTimestamp().getTime() - startPoint.getTimestamp().getTime()) / 1000;
            
            // Check minimum distance condition: Transport distance ≥ t_se_min_distance
            // NOTE: This is NOT part of endpoint detection, but part of transport validity
            // Convert transportDistanceKm (in km) to meters for comparison with tSeMinDistance (in meters)
            boolean distanceConditionMet = (deviceInfo.getTSeMinDistance() == null) || 
                                          (transportDistanceKm * 1000 >= deviceInfo.getTSeMinDistance());
            
            // Check minimum time condition: Transport duration ≥ t_se_min_time
            // NOTE: This is NOT part of endpoint detection, but part of transport validity
            boolean timeConditionMet = (deviceInfo.getTSeMinTime() == null) || 
                                      (transportTime >= deviceInfo.getTSeMinTime());
            
            // Determine if transport is valid based on the conditions
            // NOTE: This is NOT part of endpoint detection, but part of transport validity
            boolean isTransportValid = distanceConditionMet && timeConditionMet;
            
            // Log the transport validity conditions for debugging
            log.debug("Transport validity for device {}: distanceConditionMet={}, timeConditionMet={}, isTransportValid={}", 
                    context.getDeviceId(), distanceConditionMet, timeConditionMet, isTransportValid);
            log.debug("Transport details for device {}: distance={}km, time={}s, tSeMinDistance={}, tSeMinTime={}", 
                    context.getDeviceId(), transportDistanceKm, transportTime, 
                    deviceInfo.getTSeMinDistance(), deviceInfo.getTSeMinTime());
            
            // Get current transport times (last valid transport count)
            int transportTimes = getTransportTimesForToday(context.getDeviceId());
            
            // For display purposes, use the incremented count for valid transports, 
            // and the current count for invalid transports
            int displayTransportTimes = transportTimes;
            if (isTransportValid) {
                displayTransportTimes++; // Increment for this new valid transport
            }
            
            // Log end point event
            TransportEventLogger.logEndPointEvent(context.getDeviceId(), startPoint, endPoint, 
                                                transportDistanceKm * 1000, transportTime, displayTransportTimes, 
                                                isTransportValid ? 1 : 0);
            
            // Log detailed radar data for end point analysis
            TransportEventLogger.logEndPointRadarData(context.getDeviceId(), context, deviceInfo);
            
            // Create transport record with precise distance
            createTransportRecord(context, startPoint, endPoint, transportDistanceKm * 1000, transportTime, displayTransportTimes, isTransportValid ? 1 : 0);
            
            // Send transport times message to device
            if (deviceCommandService != null && deviceCommandService.isDeviceConnected(context.getDeviceId())) {
                // For all transports (valid or invalid), send the appropriate transport count
                String message = String.format("%03d", displayTransportTimes); // 001, 002, etc.
                
                // Try to send the command with acknowledgment waiting and retry logic
                boolean commandSent = false;
                int retryCount = 0;
                final int maxRetries = 6;
                
                while (!commandSent && retryCount < maxRetries) {
                    if (retryCount > 0) {
                        // Add a delay between retries
                        try {
                            Thread.sleep(6000); // Wait 6 seconds between retries
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            break;
                        }
                        log.debug("Retrying to send transport end message '{}' to device {} (attempt {})", 
                                message, context.getDeviceId(), retryCount + 1);
                    }
                    
                    // Before sending the new command, clear the previous display state
                    // This prevents visual artifacts or incorrect displays
                    clearDeviceDisplay(context.getDeviceId());
                    
                    // Add a delay after clearing display before sending the new command
                    try {
                        Thread.sleep(300);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        break;
                    }
                    
                    // Send command and wait for acknowledgment
                    commandSent = deviceCommandService.sendRemoteDisplayContentWithAck(context.getDeviceId(), message);
                    retryCount++;
                }
                
                if (commandSent) {
                    log.info("Sent transport end message '{}' to device {} (acknowledged)", message, context.getDeviceId());
                } else {
                    log.warn("Failed to send transport end message '{}' to device {} after {} attempts (no acknowledgment)", 
                            message, context.getDeviceId(), maxRetries);
                }
                
                // Log device command event
                TransportEventLogger.logDeviceCommandEvent(context.getDeviceId(), message, 
                                                         isTransportValid ? "Valid transport count message" : "Invalid transport count message");
            }
            
            // Clear the start point and GPS points as it's now paired
            context.setHasUnpairedStart(false);
            context.setStartPoint(null);
            context.clearGpsPoints();
            
            log.info("Recorded transport segment for device {}: startRecordId={}, endRecordId={}, distance={}km, time={}s, transportTimes={}, status={}", 
                    context.getDeviceId(), startPoint.getRecordId(), endPoint.getRecordId(), 
                    transportDistanceKm, transportTime, displayTransportTimes, isTransportValid ? "valid" : "invalid");
        } catch (Exception e) {
            log.error("Error checking end point for device {}", context.getDeviceId(), e);
            // Clear the start point and GPS points to avoid accumulation of invalid states
            context.setHasUnpairedStart(false);
            context.setStartPoint(null);
            context.clearGpsPoints();
        }
    }
    
    // ==================== START POINT CONDITION CHECKS ====================
    
    /**
     * Check if all required parameters for start point detection are available
     */
    private boolean hasRequiredStartPointParameters(DtuFtpInfo deviceInfo) {
        return deviceInfo.getTSpeed() != null && 
               deviceInfo.getTTimeZone() != null && 
               deviceInfo.getTTimes() != null &&
               deviceInfo.getTEsMinDistance() != null;
    }
    
    /**
     * Check if speed condition is met (speed <= t_speed)
     */
    private boolean isSpeedConditionMet(DtuFtpMessageDetail messageDetail, DtuFtpInfo deviceInfo) {
        // Handle null speed values
        if (messageDetail.getSpeed() == null || deviceInfo.getTSpeed() == null) {
            log.debug("Speed condition check: message speed={} or device t_speed={} is null", 
                    messageDetail.getSpeed(), deviceInfo.getTSpeed());
            return false;
        }
        
        // Convert BigDecimal to double for comparison with Float
        double messageSpeed = messageDetail.getSpeed().doubleValue();
        double maxSpeed = deviceInfo.getTSpeed().doubleValue();
        
        boolean result = messageSpeed <= maxSpeed;
        log.debug("Speed condition check: message speed={} <= t_speed={} = {}", 
                messageSpeed, maxSpeed, result);
        return result;
    }
    
    /**
     * Check if radar distance condition is met (radar distance <= t_radar_distance)
     */
    private boolean isRadarDistanceConditionMet(DtuFtpMessageDetail messageDetail, DtuFtpInfo deviceInfo) {
        // Handle null radar distance values
        if (messageDetail.getRadarDistance() == null || deviceInfo.getTRadarDistance() == null) {
            log.debug("Radar distance condition check: message radarDistance={} or device t_radar_distance={} is null", 
                    messageDetail.getRadarDistance(), deviceInfo.getTRadarDistance());
            return true; // Allow if either value is null
        }
        
        double messageRadarDistance = messageDetail.getRadarDistance();
        double maxRadarDistance = deviceInfo.getTRadarDistance().doubleValue();
        
        // Handle special radar distance values:
        // 0 = infinity (always valid)
        // 65535 = no signal (treat as valid for this check)
        if (messageRadarDistance == 0 || messageRadarDistance == 65535) {
            log.debug("Radar distance is special value (0=infinity or 65535=no signal), condition always met: {}", messageRadarDistance);
            return true;
        }
        
        boolean result = messageRadarDistance <= maxRadarDistance;
        log.debug("Radar distance condition check: message radarDistance={} <= t_radar_distance={} = {}", 
                messageRadarDistance, maxRadarDistance, result);
        return result;
    }
    
    /**
     * Check if vibration count condition is met (count >= t_times in t_time_zone window)
     * This method handles duplicate timestamps by counting only one vibration record per unique timestamp
     */
    private boolean isVibrationCountConditionMet(TransportContext context, DtuFtpInfo deviceInfo) {
        // Instead of just counting records, we need to count records within the time window
        if (deviceInfo.getTTimeZone() == null || deviceInfo.getTTimes() == null) {
            log.debug("Missing vibration parameters for device {}", context.getDeviceId());
            return false;
        }
        
        // Get the current time
        Date now = new Date();
        long timeWindowMs = deviceInfo.getTTimeZone() * 1000L;
        
        // Use a Set to track unique timestamps to avoid counting duplicate timestamps multiple times
        java.util.Set<Long> uniqueTimestamps = new java.util.HashSet<>();
        
        // Count vibration records within the time window, deduplicating by timestamp
        for (VibrationRecord record : context.getVibrationRecords()) {
            if (record.getTimestamp() != null) {
                long timeDiff = now.getTime() - record.getTimestamp().getTime();
                if (timeDiff <= timeWindowMs && timeDiff >= 0) {
                    // Add timestamp to set (automatically handles deduplication)
                    uniqueTimestamps.add(record.getTimestamp().getTime());
                }
            }
        }
        
        // The count is now the number of unique timestamps
        int count = uniqueTimestamps.size();
        
        boolean result = count >= deviceInfo.getTTimes();
        log.debug("Vibration count check for device {}: unique timestamps count={} in last {}s, required={}, result={}", 
                context.getDeviceId(), count, deviceInfo.getTTimeZone(), deviceInfo.getTTimes(), result);
        return result;
    }
    
    /**
     * Check if start point distance condition is met (distance from previous end point >= t_es_min_distance)
     */
    private boolean isStartPointDistanceConditionMet(TransportContext context, DtuFtpMessageDetail messageDetail, DtuFtpInfo deviceInfo) {
        // Skip this check for the first transport of the day
        // We check if this is the first transport by looking at today's valid transport count
        int todayTransportCount = getTransportTimesForToday(context.getDeviceId());
        if (todayTransportCount == 0) {
            // This is the first transport of the day, no need to check distance from previous end point
            return true;
        }
        
        // This is not the first transport of the day, check the distance condition
        // We need to find the last valid transport segment for this device
        DtuTransportInfo lastTransport = getLastValidTransportSegment(context.getDeviceId());
        if (lastTransport == null) {
            // No previous valid transport found, no need to check distance
            return true;
        }
        
        // Check the distance condition
        if (lastTransport.getEndLatitude() != null && lastTransport.getEndLongitude() != null &&
            messageDetail.getLatitude() != null && messageDetail.getLongitude() != null) {
            
            // Calculate distance between previous end point and current start point
            java.math.BigDecimal lastEndLat = lastTransport.getEndLatitude() != null ? 
                java.math.BigDecimal.valueOf(lastTransport.getEndLatitude()) : null;
            java.math.BigDecimal lastEndLon = lastTransport.getEndLongitude() != null ? 
                java.math.BigDecimal.valueOf(lastTransport.getEndLongitude()) : null;
                
            double distanceFromLastEnd = calculateHaversineDistance(
                    lastEndLat, lastEndLon,
                    messageDetail.getLatitude(), messageDetail.getLongitude());
            
            // Check if distance is greater than t_es_min_distance
            if (deviceInfo.getTEsMinDistance() != null && distanceFromLastEnd < deviceInfo.getTEsMinDistance()) {
                log.debug("Start point rejected for device {}: Distance from last end point ({:.2f}m) is less than t_es_min_distance ({:.2f}m)", 
                        context.getDeviceId(), distanceFromLastEnd, (double)deviceInfo.getTEsMinDistance());
                return false;
            }
        }
        return true;
    }
    
    // ==================== END POINT CONDITION CHECKS ====================
    
    /**
     * Check if radar condition is met (radar distance > t_radar_distance maintained for t_radar_time seconds)
     */
    private boolean isRadarConditionMet(TransportContext context, DtuFtpInfo deviceInfo) {
        // Handle special radar distance values:
        // 0 = infinity (always valid)
        // 65535 = no signal (never valid)
        java.math.BigDecimal tRadarDistance = deviceInfo.getTRadarDistance();
        Integer tRadarTime = deviceInfo.getTRadarTime();
        
        log.debug("Checking radar condition for device {}: tRadarDistance={}, tRadarTime={}", 
                context.getDeviceId(), tRadarDistance, tRadarTime);
        
        // Check if we have all required parameters
        if (tRadarDistance == null || tRadarTime == null) {
            log.debug("Missing radar parameters for device {}, skipping end point check", context.getDeviceId());
            return false;
        }
        
        double minRadarDistance = tRadarDistance.doubleValue();
        
        if (minRadarDistance == 0) {
            // Radar distance of 0 means infinity - always valid
            log.debug("Radar distance is 0 (infinity), condition always met for device {}", context.getDeviceId());
            return true;
        } else if (minRadarDistance == 65535) {
            // Radar distance of 65535 means no signal - never valid
            log.debug("Radar distance is 65535 (no signal), condition never met for device {}", context.getDeviceId());
            return false;
        } else {
            // Normal radar distance check with duration requirement
            log.debug("Performing normal radar distance check with duration requirement");
            log.debug("Current radar records count for device {}: {}", 
                    context.getDeviceId(), context.getRadarRecords().size());
            
            boolean result = context.isRadarConditionMet(minRadarDistance, tRadarTime);
            log.debug("Radar condition result for device {}: {}", context.getDeviceId(), result);
            return result;
        }
    }
    
    /**
     * Create and save a transport record
     */
    private void createTransportRecord(TransportContext context, TransportPoint startPoint, 
                                      TransportPoint endPoint, double transportDistance, long transportTime, 
                                      int times, int status) {
        try {
            DtuTransportInfo transportInfo = new DtuTransportInfo();
            transportInfo.setDtuNumber(context.getDeviceId());
            transportInfo.setStartLongitude(startPoint.getLongitude() != null ? startPoint.getLongitude().doubleValue() : null);
            transportInfo.setStartLatitude(startPoint.getLatitude() != null ? startPoint.getLatitude().doubleValue() : null);
            transportInfo.setEndLongitude(endPoint.getLongitude() != null ? endPoint.getLongitude().doubleValue() : null);
            transportInfo.setEndLatitude(endPoint.getLatitude() != null ? endPoint.getLatitude().doubleValue() : null);
            transportInfo.setTransportDistance(transportDistance); // Store precise distance in meters
            transportInfo.setStartTime(startPoint.getTimestamp());
            transportInfo.setEndTime(endPoint.getTimestamp());
            transportInfo.setStartLogid(startPoint.getRecordId());
            transportInfo.setEndLogid(endPoint.getRecordId());
            transportInfo.setTimes(times); // Set the transport times
            transportInfo.setStatus(status); // Set the transport status (0=invalid, 1=valid)
            
            // Set create and update times
            Date now = new Date();
            transportInfo.setCreateTime(now);
            transportInfo.setUpdateTime(now);
            transportInfo.setDelFlag("F");
            
            // Save to database
            if (dtuTransportInfoService != null) {
                DtuTransportInfo savedTransportInfo = dtuTransportInfoService.saveTransportInfo(transportInfo);
                log.info("Saved transport info to database with ID: {}", savedTransportInfo.getId());
                
                // Log transport record event
                TransportEventLogger.logTransportRecordEvent(context.getDeviceId(), savedTransportInfo);
                
                // Send message to RabbitMQ
                if (rabbitTemplate != null) {
                    try {
                        // Send message without expiration (TTL has been removed)
                        rabbitTemplate.convertAndSend("dtu.transport.exchange", "dtu.transport.queue", 
                                savedTransportInfo);
                        log.info("Sent transport info to RabbitMQ: {} without TTL", 
                                savedTransportInfo.getId());
                    } catch (Exception e) {
                        log.error("Error sending transport info to RabbitMQ", e);
                    }
                }
            } else {
                log.warn("DtuTransportInfoService is null, unable to save transport info");
            }
        } catch (Exception e) {
            log.error("Error creating transport record for device {}", context.getDeviceId(), e);
        }
    }
    
    /**
     * Calculate distance between two points using Haversine formula
     * 
     * @param lat1 Latitude of point 1
     * @param lon1 Longitude of point 1
     * @param lat2 Latitude of point 2
     * @param lon2 Longitude of point 2
     * @return Distance in meters
     */
    private static double calculateHaversineDistance(java.math.BigDecimal lat1, java.math.BigDecimal lon1, java.math.BigDecimal lat2, java.math.BigDecimal lon2) {
        // Check for null values
        if (lat1 == null || lon1 == null || lat2 == null || lon2 == null) {
            return 0.0;
        }
        
        // Convert BigDecimal to double for calculation
        double lat1Double = lat1.doubleValue();
        double lon1Double = lon1.doubleValue();
        double lat2Double = lat2.doubleValue();
        double lon2Double = lon2.doubleValue();
        
        final int EARTH_RADIUS = 6371; // Earth radius in kilometers
        
        // Convert latitude and longitude from degrees to radians
        double lat1Rad = Math.toRadians(lat1Double);
        double lon1Rad = Math.toRadians(lon1Double);
        double lat2Rad = Math.toRadians(lat2Double);
        double lon2Rad = Math.toRadians(lon2Double);
        
        // Haversine formula
        double deltaLat = lat2Rad - lat1Rad;
        double deltaLon = lon2Rad - lon1Rad;
        double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
                   Math.cos(lat1Rad) * Math.cos(lat2Rad) *
                   Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        
        // Distance in meters
        return EARTH_RADIUS * c * 1000;
    }
    
    /**
     * Get the last completed transport segment for a device
     * 
     * @param deviceId the device ID
     * @return the last transport segment or null if not found
     */
    private DtuTransportInfo getLastTransportSegment(String deviceId) {
        try {
            if (dtuTransportInfoService != null) {
                // Query the database for the most recent transport segment for this device
                return dtuTransportInfoService.getOne(
                        new com.mybatisflex.core.query.QueryWrapper()
                                .where("dtu_number = ?", deviceId)
                                .and("del_flag = ?", "F")
                                .orderBy("create_time", false) // Order by create_time DESC to get the most recent
                                .limit(1));
            }
        } catch (Exception e) {
            log.error("Error getting last transport segment for device {}", deviceId, e);
        }
        return null;
    }
    
    /**
     * Get the last valid transport segment for a device
     * 
     * @param deviceId the device ID
     * @return the last valid transport segment or null if not found
     */
    private DtuTransportInfo getLastValidTransportSegment(String deviceId) {
        try {
            if (dtuTransportInfoService != null) {
                // Query the database for the most recent valid transport segment for this device
                return dtuTransportInfoService.getOne(
                        new com.mybatisflex.core.query.QueryWrapper()
                                .where("dtu_number = ?", deviceId)
                                .and("del_flag = ?", "F")
                                .and("status = ?", 1) // Only valid transports
                                .orderBy("create_time", false) // Order by create_time DESC to get the most recent
                                .limit(1));
            }
        } catch (Exception e) {
            log.error("Error getting last valid transport segment for device {}", deviceId, e);
        }
        return null;
    }
    
    /**
     * Get the transport times for today for a specific device
     * Only counts valid transports (status = 1)
     * 
     * @param deviceId the device ID
     * @return the transport times for today
     */
    private int getTransportTimesForToday(String deviceId) {
        try {
            if (dtuTransportInfoService != null) {
                // Get today's date range
                LocalDate today = LocalDate.now();
                LocalDateTime startOfDay = today.atStartOfDay();
                LocalDateTime endOfDay = today.atTime(23, 59, 59);
                
                // Convert to Date objects
                Date startDate = Date.from(startOfDay.atZone(ZoneId.systemDefault()).toInstant());
                Date endDate = Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant());
                
                // Query the database for valid transport segments for this device today
                List<DtuTransportInfo> todayTransports = 
                    dtuTransportInfoService.list(
                        new com.mybatisflex.core.query.QueryWrapper()
                                .where("dtu_number = ?", deviceId)
                                .and("del_flag = ?", "F")
                                .and("status = ?", 1) // Only count valid transports
                                .and("create_time >= ?", startDate)
                                .and("create_time <= ?", endDate)
                                .orderBy("create_time", true)); // Order by create_time ASC
                
                // Return the count of today's valid transports
                return todayTransports.size();
            }
        } catch (Exception e) {
            log.error("Error getting transport times for today for device {}", deviceId, e);
        }
        return 0;
    }
    
    /**
     * Clear the device display by sending a blank command
     * This prevents visual artifacts when switching between different display commands
     * 
     * @param deviceId the device ID
     */
    private void clearDeviceDisplay(String deviceId) {
        try {
            log.debug("Clearing display for device {}", deviceId);
            
            // Send a blank display command to clear the previous state
            // Using "   " (three spaces) to clear the display
            String blankCommand = "   "; // Three spaces
            
            // Send the blank command with enhanced reliability
            boolean result = deviceCommandService.sendRemoteDisplayContent(deviceId, blankCommand);
            
            if (result) {
                log.debug("Successfully sent display clear command to device {}", deviceId);
            } else {
                log.warn("Failed to send display clear command to device {}", deviceId);
            }
            
            // Add a delay after sending the clear command
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        } catch (Exception e) {
            log.error("Error clearing display for device {}: {}", deviceId, e.getMessage(), e);
        }
    }
}